﻿/**
 * The AudioTrack Library by Chris J. Rock. Aug 10, 2011
 * Check out chrisjrock.net and be a king among men.
 *
 * Copyright (c) 2011 Chris J. Rock
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 **/


package net.chrisjrock.sound.audioTrack.mixer 
{
	import flash.media.SoundTransform;
	
	import net.chrisjrock.sound.audioTrack.data.AudioMixerData;
	import net.chrisjrock.sound.audioTrack.data.IAudioMixerData;
	import net.chrisjrock.sound.audioTrack.data.IAudioTrackData;
	import net.chrisjrock.sound.audioTrack.events.AudioMixerEvent;
	import net.chrisjrock.sound.audioTrack.events.AudioTrackEvent;
	import net.chrisjrock.sound.audioTrack.track.AbstractTrack;
	import net.chrisjrock.sound.audioTrack.track.IAudioTrack;
	
	/**
	 * An abstract super class that handles all basic mixer functionality.
	 * @author Christopher J. Rock
	 */
	public class AbstractMixer extends AbstractTrack implements IAudioMixer 
	{
		private var _soloTrack:IAudioTrack;
		private var _bubbleSolo:Boolean = true;
		
		public function AbstractMixer(trackId:String) 
		{
			super(trackId);
		}
		
		/**
		 * @inheritDoc
		 */		
		public function disposeAll():void 
		{
			throw (new Error("This is a virtual method and must be overridden."));
		}
		
		/**
		 * @inheritDoc
		 */		
		override public function cloneData ():IAudioTrackData
		{
			var trackData:IAudioMixerData = IAudioMixerData(super.cloneData());
			trackData.soloTrack = _soloTrack;
			return trackData;
		}
		
		/**
		 * @inheritDoc
		 */		
		override public function updateTransform (transform:SoundTransform = null):void 
		{
			super.updateTransform(transform);
			
			if (transform && !_mute) 
			{
				updateEffect();
			}
		}
		
		/**
		 * @inheritDoc
		 */		
		public function addTrack (track:IAudioTrack):void 
		{
			if (track == null)
			{
				throw (new Error("Cannot add a null value track."));
			}
			
			if (hasTrack(track)) 
			{
				//The track is already a member of the mixer.
				return;
			}
			
			addTrackToCollection(track);
			track.mixer = this;
			
			track.addEventListener(AudioTrackEvent.SOLO_TOGGLE , trackSoloToggleHandler);
			if (track.solo)
			{
				applyTrackSoloToggle(track);
			}
			
			if (track is IAudioMixer) 
			{
				track.addEventListener(AudioMixerEvent.SOLO_TOGGLE , mixerSoloToggleHandler);
				if (!track.solo && IAudioMixer(track).soloTrack)
				{
					applyTrackSoloToggle(track);
				}
			}
		}
		
		/**
		 * @inheritDoc
		 */		
		public function removeTrack (track:IAudioTrack):void 
		{
			if (track == null)
			{
				throw (new Error("Cannot remove a null value track."));
			}
			
			if (!hasTrack(track))
			{
				throw (new Error("A mixer cannot remove a track if the track has not been added to the mixer."));
			}
			
			removeTrackFromCollection(track);
			
			track.removeEventListener(AudioTrackEvent.SOLO_TOGGLE , trackSoloToggleHandler);
			if (track is IAudioMixer)
			{
				track.removeEventListener(AudioMixerEvent.SOLO_TOGGLE , mixerSoloToggleHandler);
			}
			track.mixer = null;
		}
		
		/**
		 * @inheritDoc
		 */		
		public function hasTrack (track:IAudioTrack):Boolean
		{
			throw (new Error("This is a virtual method and must be overridden."));
		}
		
		/**
		 * @inheritDoc
		 */		
		public function hasTrackId (trackId:String):Boolean
		{
			throw (new Error("This is a virtual method and must be overridden."));
		}
		
		/**
		 * @inheritDoc
		 */		
		public function getTrack (trackId:String):IAudioTrack
		{
			throw (new Error("This is a virtual method and must be overridden."));
		}
		
		protected function addTrackToCollection (track:IAudioTrack):void
		{
			throw (new Error("This is a virtual method and must be overridden."));
		}
		
		protected function removeTrackFromCollection (track:IAudioTrack):void
		{
			throw (new Error("This is a virtual method and must be overridden."));
		}
		
		override protected function applyMixerEffectToTransform (transform:SoundTransform):void
		{
			if (_effect.mute)
			{
				transform.volume = 0;
			}
			else if (mixer && mixer.effect)
			{
				var inheritance:IAudioMixerData = IAudioMixerData(mixer.effect);
				if (inheritance.soloTrack != null && inheritance.soloTrack != this && inheritance.soloTrack != _soloTrack)
				{
					transform.volume = 0;
				}
			}
		}
		
		/**
		 * @inheritDoc
		 */
		override protected function instantiateTrackData ():IAudioTrackData
		{
			return new AudioMixerData();
		}
		
		/**
		 * @inheritDoc
		 */
		override protected function refreshEffectData(inheritSolo:Boolean = true):void 
		{
			super.refreshEffectData(inheritSolo);
			
			var mixerEffect:IAudioMixerData = IAudioMixerData(_effect);
			mixerEffect.soloTrack = _soloTrack;
			
			if (!inheritSolo || mixer == null || mixer.effect == null)
			{
				return;
			}
			
			var inheritance:IAudioMixerData = IAudioMixerData(mixer.effect);
			if (inheritance.soloTrack == this)
			{
				return;
			}
			
			mixerEffect.soloTrack = _soloTrack = inheritance.soloTrack;
		}
		
		/**
		 * @inheritDoc
		 */
		override protected function applyEffect ():void
		{
			dispatchEvent(new AudioMixerEvent(AudioMixerEvent.UPDATE));
		}
		
		override protected function updateSolo ():void
		{
			if (solo && (_soloTrack || _mute)) 
			{
				_soloTrack = null;
				
				var mutePrev:Boolean = _mute;
				_mute = false;
				
				updateEffect();
				
				if (mutePrev)
				{
					dispatchEvent(new AudioTrackEvent(AudioTrackEvent.MUTE_TOGGLE));
				}
			}
			
			dispatchEvent(new AudioTrackEvent(AudioTrackEvent.SOLO_TOGGLE));
		}
		
		private function applyTrackSoloToggle (track:IAudioTrack):void
		{
			if (_soloTrack && track == _soloTrack)
			{
				var event:AudioMixerEvent = new AudioMixerEvent(AudioMixerEvent.SOLO_TOGGLE , _soloTrack);
				_soloTrack = null;
				updateEffect(false);
				if (_bubbleSolo)
				{
					dispatchEvent(event);
				}
			}
			else if (track.solo)
			{
				if (_soloTrack)
				{
					_soloTrack.solo = false;
				}
				_soloTrack = track;
				_mute = false;
				updateEffect(false);
				if (_bubbleSolo)
				{
					dispatchEvent(new AudioMixerEvent(AudioMixerEvent.SOLO_TOGGLE , _soloTrack));
				}
			}
		}
		
		private function trackSoloToggleHandler (event:AudioTrackEvent):void 
		{
			applyTrackSoloToggle(IAudioTrack(event.target));
		}
		
		private function mixerSoloToggleHandler (event:AudioMixerEvent):void 
		{
			applyTrackSoloToggle(event.track);
		}
		
		/**
		 * @inheritDoc
		 */		
		public function get soloTrack ():IAudioTrack
		{
			return _soloTrack;
		}
		
		/**
		 * Determines whether solo status will be bubbled to the parent mixer. 
		 * @return 
		 * 
		 */		
		public function get bubbleSolo ():Boolean
		{
			return _bubbleSolo;
		}
		
		/**
		 * @private
		 */		
		public function set bubbleSolo (value:Boolean):void 
		{
			_bubbleSolo = value;
			if (!_bubbleSolo && _soloTrack)
			{
				dispatchEvent(new AudioMixerEvent(AudioMixerEvent.SOLO_TOGGLE , _soloTrack));
			}
		}
		
	}
	
}